/*********************************************************
          File Name:sock.c
          Author: Abby Cin
          Mail: abbytsing@gmail.com
          Created Time: Mon 15 Jun 2015 02:30:17 PM CST
**********************************************************/

#include "sock.h"
#include <strings.h>
#include <stdio.h>

int nonblock_socket(int sfd)
{
    int flags, res;

    flags = fcntl(sfd, F_GETFL, 0);
    if (flags == -1)
        return -1;
    flags |= O_NONBLOCK;
    res = fcntl(sfd, F_SETFL, flags);
    if (res == -1)
        return -1;

    return 0;
}

int mysocket(int op, const char *host, const char *port, int backlog, socklen_t * addr_len)
{
    struct addrinfo *current = NULL;
    struct addrinfo hints;
    struct addrinfo *result = NULL;
    int sockfd = 0;
    int option = 1;

    bzero(&hints, sizeof(struct addrinfo));
    hints.ai_canonname = NULL;
    hints.ai_addr = NULL;
    hints.ai_next = NULL;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_family = AF_INET;        /* IPv4 */

    getaddrinfo(host, port, &hints, &result);

    for (current = result; current != NULL; current = current->ai_next) {
        sockfd = socket(current->ai_family, current->ai_socktype, current->ai_protocol);
        if (sockfd == -1)
            continue;
        // SO_KEEPALIVE and SO_REUSEPORT is optional for client
        if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &option, sizeof(option)) == -1) {
            close(sockfd);
            freeaddrinfo(result);
            return -1;
        }
        if(op == SERVER)
        {
            if (bind(sockfd, current->ai_addr, current->ai_addrlen) == 0)
                break;
        }
        else
        {
            if(connect(sockfd, current->ai_addr, current->ai_addrlen) == 0)
                break;
        }
        close(sockfd);
    }
    if(op == SERVER)
    {
        if (current != NULL) {
#ifdef __linux__
            if (nonblock_socket(sockfd) < 0) {
                close(sockfd);
                return -1;
            }
#endif
            if (listen(sockfd, backlog) == -1) {
                freeaddrinfo(result);
                return -1;
            }
        }
    }
    if (current != NULL && addr_len != NULL)
        *addr_len = current->ai_addrlen;

    freeaddrinfo(result);
    return (current == NULL) ? -1 : sockfd;
}

int unix_socket(const char *unix_domain, int baclog)
{
    int sockfd = 0;
    struct sockaddr_un  addr;       /* unix domain socket */
    remove(unix_domain);    /* ignore error */

    sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
    if(sockfd == -1)
        return -1;
    bzero(&addr, sizeof(addr));
    addr.sun_family = AF_UNIX;

    strncpy(addr.sun_path, unix_domain, strlen(unix_domain));

    if(bind(sockfd, (struct sockaddr *)&addr, sizeof(struct sockaddr_un)) == -1)
        return -1;
    if(listen(sockfd, baclog) == -1)
        return -1;

    return sockfd;
}

int unix_connect(const char *unix_domain)
{
    int sockfd;
    struct sockaddr_un addr;

    sockfd = socket(AF_UNIX,SOCK_STREAM, 0);
    if(sockfd == -1)
        return -1;
    memset(&addr, 0, sizeof(struct sockaddr_un));
    addr.sun_family = AF_UNIX;

    strncpy(addr.sun_path, unix_domain, strlen(unix_domain));

    if(connect(sockfd, (struct sockaddr *)&addr, sizeof(struct sockaddr_un))==-1)
        return -1;
    return sockfd;
}
